package com.ydlclass.channelhandler.handler;

import com.ydlclass.ServiceConfig;
import com.ydlclass.YrpcBootstrap;
import com.ydlclass.core.ShutDownHolder;
import com.ydlclass.enumeration.RequestType;
import com.ydlclass.enumeration.RespCode;
import com.ydlclass.protection.RateLimiter;
import com.ydlclass.protection.TokenBuketRateLimiter;
import com.ydlclass.transport.message.RequestPayload;
import com.ydlclass.transport.message.YrpcRequest;
import com.ydlclass.transport.message.YrpcResponse;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.SocketAddress;
import java.util.Map;

/**
 * 这个类是一个基于 Netty 的网络通信处理器，主要作用是处理客户端发送的 YrpcRequest 请求，
 * 并根据请求内容执行相应的服务方法调用，最终生成并返回 YrpcResponse 响应。
 */
@Slf4j
public class MethodCallHandler extends SimpleChannelInboundHandler<YrpcRequest> {
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, YrpcRequest yrpcRequest) throws Exception {
    
        // 1、先封装部分响应
        YrpcResponse yrpcResponse = new YrpcResponse();
        yrpcResponse.setRequestId(yrpcRequest.getRequestId());
        yrpcResponse.setCompressType(yrpcRequest.getCompressType());
        yrpcResponse.setSerializeType(yrpcRequest.getSerializeType());
    
        // 2、 获得通道
        Channel channel = channelHandlerContext.channel();
        
        // 3、查看关闭的挡板是否打开，如果挡板已经打开，返回一个错误的响应
        if(ShutDownHolder.BAFFLE.get()){
            yrpcResponse.setCode(RespCode.BECOLSING.getCode());
            channel.writeAndFlush(yrpcResponse);
            return;
        }
        
        // 4、计数器加一
        ShutDownHolder.REQUEST_COUNTER.increment();
        
        // 5、完成限流相关的操作

        //获取地址
        SocketAddress socketAddress = channel.remoteAddress();

        //每一个地址（socketAddress）匹配一个限流器
        Map<SocketAddress, RateLimiter> everyIpRateLimiter =
            YrpcBootstrap.getInstance().getConfiguration().getEveryIpRateLimiter();
        
        RateLimiter rateLimiter = everyIpRateLimiter.get(socketAddress); //获取限流器
        if (rateLimiter == null) {
            rateLimiter = new TokenBuketRateLimiter(10, 10); //令牌桶限流器
            everyIpRateLimiter.put(socketAddress, rateLimiter);
        }

        boolean allowRequest = rateLimiter.allowRequest();
        
        // 6、处理请求的逻辑

        // 处理限流
        if (!allowRequest) {
            // 需要封装响应并且返回了
            yrpcResponse.setCode(RespCode.RATE_LIMIT.getCode());
            
            // 处理心跳请求
        } else if (yrpcRequest.getRequestType() == RequestType.HEART_BEAT.getId()) {
            // 需要封装响应并且返回
           yrpcResponse.setCode(RespCode.SUCCESS_HEART_BEAT.getCode());
           
           // 处理普通请求
        } else {
            /** ---------------具体的调用过程--------------**/
            // （1）获取负载内容
            RequestPayload requestPayload = yrpcRequest.getRequestPayload();

            // （2）根据负载内容进行方法调用
            try {
                Object result = callTargetMethod(requestPayload);//通过反射获取了方法的具体返回值 ---> 接下来要对响应进行封装处理
                if (log.isDebugEnabled()) {
                    log.debug("请求【{}】已经在服务端完成方法调用。", yrpcRequest.getRequestId());
                }
                // （3）封装响应   我们是否需要考虑另外一个问题，响应码，响应类型
                yrpcResponse.setCode(RespCode.SUCCESS.getCode());
                //将方法调用返回的结果进行封装
                yrpcResponse.setBody(result);
            } catch (Exception e){
                log.error("编号为【{}】的请求在调用过程中发生异常。",yrpcRequest.getRequestId(),e);
                yrpcResponse.setCode(RespCode.FAIL.getCode());
            }
        }

        /**
         * 7、写出响应
         * yrpcResponse不是bytebuf对象，不能直接通过channel进行传输
         * 此时netty会在管道中自动寻找匹配的编码器(YrpcResponseEncoder)，并编码成bytebuf
         * 然后在网络中进行传输。
         */
        channel.writeAndFlush(yrpcResponse);
        
        // 8、计数器减一
        ShutDownHolder.REQUEST_COUNTER.decrement();
    }
    
    /**
     * 根据请求负载（RequestPayload）调用目标服务的方法，获得该方法的返回值
     * @param requestPayload 请求负载
     * @return
     */
    private Object callTargetMethod(RequestPayload requestPayload) {
        String interfaceName = requestPayload.getInterfaceName(); //接口名
        String methodName = requestPayload.getMethodName(); //方法名
        Class<?>[] parametersType = requestPayload.getParametersType(); //参数类型
        Object[] parametersValue = requestPayload.getParametersValue(); //具体的参数值
        
        // 寻找到匹配的暴露出去的具体的实现
        ServiceConfig<?> serviceConfig = YrpcBootstrap.SERVERS_LIST.get(interfaceName);

        //该接口的具体实现类
        Object refImpl = serviceConfig.getRef();

        // 通过反射调用 1、获取方法对象  2、执行invoke方法
        Object returnValue;
        try {
            Class<?> aClass = refImpl.getClass(); //获取Class对象
            Method method = aClass.getMethod(methodName, parametersType); //通过方法名和参数类型获取方法
            returnValue = method.invoke(refImpl, parametersValue); //执行方法调用
        } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
            log.error("调用服务【{}】的方法【{}】时发生了异常。", interfaceName, methodName, e);
            throw new RuntimeException(e);
        }
        return returnValue;
    }
}
